package com.avengers.core.model.generator;

import com.avengers.core.bean.enums.SymbolEnum;
import com.avengers.core.kit.StringKit;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.activerecord.dialect.OracleDialect;
import com.jfinal.plugin.activerecord.generator.MetaBuilder;
import com.jfinal.plugin.activerecord.generator.TableMeta;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;

import static com.avengers.core.bean.Numbers.INT_0;
import static com.avengers.core.bean.Numbers.INT_1;

/**
 * @author yu.wang
 * @since 2018-04-03 上午10:49
 */
public class AvengersMetaBuilder extends MetaBuilder {

    private static final Logger log = LoggerFactory.getLogger(AvengersMetaBuilder.class);
    protected Set<String> includeTables = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
    protected String modelNameStart;
    protected String modelNameEnd;

    public AvengersMetaBuilder(DataSource dataSource) {
        super(dataSource);
    }

    public void setModelNameStart(String modelNameStart) {
        this.modelNameStart = modelNameStart;
    }

    public void setModelNameEnd(String modelNameEnd) {
        this.modelNameEnd = modelNameEnd;
    }

    public void addIncludeTables(String... includeTables) {
        if (includeTables != null) {
            for (String table : includeTables) {
                if (!"".equals(table)) {
                    this.includeTables.add(table);
                }
            }
        }
    }

    /**
     * 构造 modelName，mysql 的 tableName 建议使用小写字母，多单词表名使用下划线分隔，不建议使用驼峰命名
     * oracle 之下的 tableName 建议使用下划线分隔多单词名，无论 mysql还是 oralce，tableName 都不建议使用驼峰命名
     */
    @Override
    protected String buildModelName(String tableName) {
        // 移除表名前缀仅用于生成 modelName、baseModelName，而 tableMeta.name 表名自身不能受影响
        String modelName = tableName;
        if (removedTableNamePrefixes != null) {
            for (String prefix : removedTableNamePrefixes) {
                if (modelName.startsWith(prefix)) {
                    modelName = modelName.replaceFirst(prefix, "");
                    break;
                }
            }
        }

        // 将 oralce 大写的 tableName 转成小写，再生成 modelName
        if (dialect instanceof OracleDialect) {
            modelName = modelName.toLowerCase();
        }
        modelName = StrKit.firstCharToUpperCase(StrKit.toCamelCase(modelName));

        if (StrKit.notBlank(modelNameStart)) {
            modelName = modelNameStart + modelName;
        }
        if (StringKit.notBlank(modelNameEnd)) {
            modelName = modelName + modelNameEnd;
        }
        return modelName;
    }

    /**
     * 使用 modelName 构建 baseModelName
     */
    @Override
    protected String buildBaseModelName(String modelName) {
        if (StringKit.notBlank(modelNameStart)) {
            return modelNameStart + "Base" + modelName.substring(modelNameStart.length());
        }
        return "Base" + modelName;
    }

    @Override
    protected void buildTableNames(List<TableMeta> ret) throws SQLException {
        ResultSet rs = getTablesResultSet();
        while (rs.next()) {
            String tableName = rs.getString("TABLE_NAME");

            if (isSkipTable(tableName)) {
                continue;
            }
            if (!isIncludeTable(this.includeTables, tableName)) {
                continue;
            }

            TableMeta tableMeta = new TableMeta();
            tableMeta.name = tableName;
            tableMeta.remarks = rs.getString("REMARKS");

            tableMeta.modelName = buildModelName(tableName);
            tableMeta.baseModelName = buildBaseModelName(tableMeta.modelName);
            ret.add(tableMeta);
        }
        rs.close();
    }

    @Override
    protected boolean isSkipTable(String tableName) {
        String includeRule = includeTable(excludedTables, tableName);
        if (StringKit.notBlank(includeRule)) {
            log.info("Skip table :【{}】by exclude rule:【{}】", tableName, includeRule);
            return true;
        }
        return false;
    }

    static boolean isIncludeTable(Set<String> includeTableList, String checkTableName) {
        String includeRule = includeTable(includeTableList, checkTableName);
        if (StringKit.isBlank(includeRule)) {
            log.info("Skip table(not include) :【{}】", checkTableName);
            return false;
        }
        return true;
    }

    static String includeTable(Set<String> includeTableList, String checkTableName) {
        if (null == includeTableList || includeTableList.isEmpty()) {
            return null;
        }
        Optional<String> includeRuleOptional = includeTableList.parallelStream()
                .filter(str -> {
                    final String tableName = checkTableName.toLowerCase();
                    final String includeTablaName = str.toLowerCase();
                    if (includeTablaName.equals(tableName)) {
                        return true;
                    }
                    if (includeTablaName.startsWith(SymbolEnum.asterisk.string)) {
                        if (tableName.endsWith(includeTablaName.substring(INT_1))) {
                            return true;
                        }
                    }
                    return includeTablaName.endsWith(SymbolEnum.asterisk.string) && tableName.startsWith(includeTablaName.substring(INT_0, includeTablaName.length() - INT_1));
                }).findAny();
        return includeRuleOptional.orElse(null);
    }
}
